package com.sunyard.redission.demos.web;

import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * @author zyl
 * @description
 * @since 2025/7/15
 */
@Service
@Slf4j
public class DistributedLockService {

    @Resource
    private RedissonClient redissonClient;

    /**
     * 获取可重入锁（自动续期）
     * @param lockKey 锁的key
     * @param waitTime 等待时间
     * @param leaseTime 持有时间（-1表示使用看门狗自动续期）
     * @param unit 时间单位
     * @return 是否获取成功
     */
    public boolean tryLock(String lockKey, long waitTime, long leaseTime, TimeUnit unit) {
        RLock lock = redissonClient.getLock(lockKey);
        try {
            // 尝试获取锁
            boolean acquired = lock.tryLock(waitTime, leaseTime, unit);

            if (acquired) {
                log.info("线程[{}]成功获取锁[{}]", Thread.currentThread().getName(), lockKey);
                return true;
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.error("获取锁[{}]被中断", lockKey, e);
        }
        return false;
    }

    /**
     * 释放锁
     * @param lockKey 锁的key
     */
    public void unlock(String lockKey) {
        RLock lock = redissonClient.getLock(lockKey);
        if (lock.isHeldByCurrentThread()) {
            try {
                log.info("线程[{}]释放锁[{}]", Thread.currentThread().getName(), lockKey);
                lock.unlock();
            } catch (IllegalMonitorStateException e) {
                log.error("当前线程不持有锁[{}]", lockKey, e);
            }
        }
    }

    /**
     * 演示可重入锁的使用
     */
    public void reentrantLockDemo() {
        String lockKey = "reentrant:demo:lock";

        // 第一次获取锁
        if (tryLock(lockKey, 5, -1, TimeUnit.SECONDS)) {
            try {
                log.info("外层锁业务执行...");

                // 第二次获取锁（可重入）
                if (tryLock(lockKey, 5, -1, TimeUnit.SECONDS)) {
                    try {
                        log.info("内层锁业务执行...");
                        Thread.sleep(2000); // 模拟业务处理
                    } finally {
                        unlock(lockKey); // 释放内层锁
                    }
                }

                Thread.sleep(3000); // 模拟业务处理
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            } finally {
                unlock(lockKey); // 释放外层锁
            }
        }
    }

    /**
     * 演示看门狗自动续期
     */
    public void watchdogDemo() {
        String lockKey = "watchdog:demo:lock";

        // leaseTime设为-1，启用看门狗自动续期
        if (tryLock(lockKey, 5, -1, TimeUnit.SECONDS)) {
            try {
                log.info("获取锁成功，开始执行业务...");

                // 模拟长时间业务处理（超过默认30秒）
                for (int i = 1; i <= 10; i++) {
                    Thread.sleep(5000); // 每5秒打印一次
                    log.info("业务执行中... {}秒", i * 5);

                    // 查看锁剩余时间（应该一直保持更新）
                    RLock lock = redissonClient.getLock(lockKey);
                    long ttl = lock.remainTimeToLive();
                    log.info("锁剩余时间: {}ms", ttl);
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            } finally {
                unlock(lockKey);
            }
        }
    }

    /**
     * 演示Pub/Sub通知机制
     */
    public void pubSubDemo() {
        String lockKey = "pubsub:demo:lock";

        // 线程1获取锁
        new Thread(() -> {
            if (tryLock(lockKey, 5, -1, TimeUnit.SECONDS)) {
                try {
                    log.info("线程1获取锁，将持有20秒");
                    Thread.sleep(10000);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                } finally {
                    unlock(lockKey);
                }
            }
        }, "Thread-1").start();

        // 线程2等待锁
        new Thread(() -> {
            try {
                Thread.sleep(1000); // 确保线程1先获取锁

                log.info("线程2尝试获取锁...");
                if (tryLock(lockKey, 5, 10, TimeUnit.SECONDS)) {
                    try {
                        log.info("线程2获取锁成功，将持有3秒");
                        Thread.sleep(3000);
                    } finally {
                        unlock(lockKey);
                    }
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }, "Thread-2").start();

        // 线程3等待锁
        new Thread(() -> {
            try {
                Thread.sleep(2000); // 确保线程1先获取锁

                log.info("线程3尝试获取锁...");
                if (tryLock(lockKey, 15, 10, TimeUnit.SECONDS)) {
                    try {
                        log.info("线程3获取锁成功，将持有5秒");
                        Thread.sleep(5000);
                    } finally {
                        unlock(lockKey);
                    }
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }, "Thread-3").start();
    }

    public static void main(String[] args) {
        // 当前日期
        Date currentDate = new Date();

        // 转换为LocalDateTime（Java 8+）
        LocalDateTime localDateTime = currentDate.toInstant()
                .atZone(ZoneId.systemDefault())
                .toLocalDateTime();

        // 前3天
        LocalDateTime threeDaysBefore = localDateTime.minusDays(3);
        Date beforeDate = Date.from(threeDaysBefore.atZone(ZoneId.systemDefault()).toInstant());

        // 后3天
        LocalDateTime threeDaysAfter = localDateTime.plusDays(3);
        Date afterDate = Date.from(threeDaysAfter.atZone(ZoneId.systemDefault()).toInstant());

        System.out.println("当前日期: " + currentDate);
        System.out.println("前3天: " + beforeDate);
        System.out.println("后3天: " + afterDate);
    }
}
